package com.managertrade.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.managertrade.dto.PredictRecordDTO;
import com.managertrade.dto.SimplePredictRecordDTO;
import com.managertrade.dto.TrainResponseDTO;
import com.managertrade.dto.vo.BatchDTO;
import com.managertrade.dto.vo.KlineWithIndicators;
import com.managertrade.dto.vo.PredictBatchStatsVO;
import com.managertrade.dto.vo.TrainHistoryResponseVO;
import com.managertrade.dao.mysql.entity.PredictRecord;
import com.managertrade.dao.mysql.repository.PredictRecordRepository;
import com.managertrade.util.strategy.TradeSignalUtil;
import com.managertrade.util.strategy.TrendAnalyzerUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Collections;
import java.util.List;
import java.util.UUID;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.web.client.RestTemplate;

@Service
@Slf4j
public class PredictRecordService {
    @Autowired
    RestTemplate  restTemplate;

    @Autowired
    private PredictRecordRepository repository;
    @Autowired
    private  KlineService marketService;

    private static final ObjectMapper objectMapper = new ObjectMapper();

    public PredictRecord save(PredictRecordDTO record) {
        PredictRecord predictRecord = new PredictRecord();
        BeanUtils.copyProperties(record,predictRecord);
        if(StringUtils.isEmpty(predictRecord.getBatchId())){
            predictRecord.setBatchId(UUID.randomUUID().toString());
        }
        List<KlineWithIndicators> klineWithIndicatorsAfter = fromJson(predictRecord.getAfterData());
        String actualResult = TrendAnalyzerUtils.calculateActualDirection( klineWithIndicatorsAfter);
        predictRecord.setActualDirection(actualResult);
        predictRecord.setIsSuccess(calculateIsSuccess(predictRecord.getPredictDirection(),actualResult));
        return repository.save(predictRecord);
    }
    /**
     * 判断预测方向与实际方向是否匹配，返回是否成功
     *
     * @param predictDirection 预测方向，可能是 "涨"、"跌"、"平稳"
     * @param actualDirection 实际方向，可能是 "涨"、"跌"、"平稳"
     * @return true：预测成功（匹配），false：失败，不匹配
     */
    public static boolean calculateIsSuccess(String predictDirection, String actualDirection) {
        if (predictDirection == null || actualDirection == null) {
            return false; // 任何一方为空，认为失败
        }
        // 直接判断两个方向字符串是否相等
        return predictDirection.trim().equals(actualDirection.trim());
    }
    public static List<KlineWithIndicators> fromJson(String json) {
        if (json == null || json.isEmpty()) return Collections.emptyList();
        try {
            return objectMapper.readValue(json, new TypeReference<List<KlineWithIndicators>>() {});
        } catch (Exception e) {
            e.printStackTrace();  // 或使用日志记录
            return Collections.emptyList();
        }
    }

    public List<PredictRecord> getByBatch(String batchName) {
        return repository.findByBatchName(batchName);
    }

    /**
     * 获取所有不同的批次列表（批次ID + 批次名称）
     */
    public List<BatchDTO> findDistinctBatches(Long userId) {
        return repository.findDistinctBatchesByUserId(userId);
    }
    @Transactional(readOnly = true)
    public Page<PredictRecordDTO> getPredictRecords(int page, int size, String batchId) {
        Pageable pageable = PageRequest.of(page - 1, size, Sort.by(Sort.Direction.DESC, "createdTime"));

        Page<PredictRecordDTO> byBatchIdOrAll = repository.findByBatchIdOrAll(batchId, pageable);
        return byBatchIdOrAll;
    }

    public PredictBatchStatsVO getBatchStatistics(String batchId) {
        List<SimplePredictRecordDTO> lightweightByBatchId = repository.findLightweightByBatchId(batchId);
        int total = lightweightByBatchId.size();
        int correct = (int) lightweightByBatchId.stream().filter(SimplePredictRecordDTO::getIsSuccess).count();
        int error = total - correct;
        PredictBatchStatsVO vo = new PredictBatchStatsVO();
        vo.setBatchId(batchId);
        vo.setTotal(total);
        vo.setCorrect(correct);
        vo.setError(error);
        vo.setAccuracy(total > 0 ? (double) correct / total : 0);
        return vo;
    }
    @Transactional
    public void deleteByBatchId(String batchId) {
        repository.deleteByBatchId(batchId);
    }

    public List<TrainHistoryResponseVO.HistoryItem> getTrainHistory() {
        String url = "http://localhost:5000/train/history";
        ResponseEntity<TrainHistoryResponseVO> response = restTemplate.getForEntity(url, TrainHistoryResponseVO.class);
        log.info("训练历史接口返回:{}",response);
        return  response.getBody().getHistory();
    }

    public TrainResponseDTO latestTrain() throws Exception {
        TrainResponseDTO trainResponse = null;
        Pair<Long, Long> longLongPair = getLastPeriodsTimeRange(80);
        Long start = longLongPair.getLeft();
        Long end = longLongPair.getRight();

        log.info("模型训练当前回测时间开始：" + TradeSignalUtil.formatTimestamp(start) + "，结束时间：" + TradeSignalUtil.formatTimestamp(end));

        List<KlineWithIndicators> klineList = marketService.getKlineWithIndicators("BTCUSDT", "1h", null, "ok", start, end);

        if (klineList == null || klineList.size() < 20) {
            log.warn("K线数据不足，跳过训练！");
            return trainResponse;
        }

        String jsonData = JSONArray.toJSONString(klineList, SerializerFeature.WriteMapNullValue);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> requestEntity = new HttpEntity<>(jsonData, headers);

        String flaskUrl = "http://127.0.0.1:5000/predict/realtime";

        try {
            ResponseEntity<TrainResponseDTO> response = restTemplate.exchange(
                    flaskUrl, HttpMethod.POST, requestEntity, TrainResponseDTO.class);
            trainResponse = response.getBody();
            if (trainResponse != null) {
                log.info("训练轮次: " + trainResponse.getRound());
                log.info("状态: " + trainResponse.getStatus());
                log.info("准确率: " + trainResponse.getAccuracy());
                log.info("模型权重: " + trainResponse.getCoef());
                if (trainResponse.getFutureDetails() != null) {
                    log.info("未来5根K线价格: " + trainResponse.getFutureDetails().getFuturePrices());
                    log.info("未来5根K线涨跌趋势: " + trainResponse.getFutureDetails().getFutureTrends());
                }
            } else {
                log.warn("Python训练接口返回空响应");
            }

        } catch (Exception e) {
            log.error("调用Python训练服务失败", e);
        }
        return trainResponse;
    }

    public static Pair<Long, Long> getLastPeriodsTimeRange(int count4h) {
        long fourHourMillis = 4 * 60 * 60 * 1000;
        long end = System.currentTimeMillis();
        long start = end - (count4h * fourHourMillis);
        return Pair.of(start, end);
    }
}

